Skip to content

feat: add NewBuf opt-out for the OpenAPI/TypeScript stage - #359

Merged
antoinetoussaint-byte merged 3 commits into
mainfrom
proto-newbuf-skip-openapi-option
Aug 26, 2026
Merged

feat: add NewBuf opt-out for the OpenAPI/TypeScript stage#359
antoinetoussaint-byte merged 3 commits into
mainfrom
proto-newbuf-skip-openapi-option

Conversation

@antoinetoussaint-byte

Copy link
Copy Markdown
Contributor

What

Adds a fluent (*Buf).WithoutOpenAPI() opt-out to companions/proto so a caller can skip the OpenAPI post-generation stage that runs after buf generate.

(*Buf).Generate unconditionally, after buf generate, does two OpenAPI things:

  1. If <dir>/openapi/api.swagger.json exists, moves it to standards.OpenAPIPath.
  2. For each <dir>/openapi/*.swagger.json, runs swagger2openapi then npx openapi-typescript to emit a .ts.

That's correct for services that want TypeScript types from their REST API. But a non-TypeScript service that owns a proto/ tree and ships an unrelated openapi/ REST contract now incurs it — dropping a stray .ts into a service that has no use for it on every Sync.

Change

  • New skipOpenAPI bool field on Buf plus WithoutOpenAPI() *Buf, mirroring the existing WithGeneratedRoot / WithGeneratedDirs builder methods.
  • The OpenAPI move and the OpenAPI→TypeScript loop are extracted verbatim into a guarded emitOpenAPIArtifacts method; WithoutOpenAPI short-circuits it and leaves any openapi/*.swagger.json inputs untouched.
  • buf dep update / buf generate and the generated-dir cleanup are unchanged. Default behavior is unchanged — the OpenAPI pipeline still runs unless a caller opts out.

Test

New offline unit tests in companions/proto (following proto_dependencies_test.go):

  • With WithoutOpenAPI(), given a dir containing openapi/*.swagger.json, emitOpenAPIArtifacts leaves every swagger file byte-for-byte untouched and produces no .ts (and no intermediate .json). A nil runner is passed deliberately: reaching the end without a panic proves the stage short-circuited before spawning any process.
  • The builder contract: NewBuf leaves the stage enabled by default, and WithoutOpenAPI() sets the flag and returns the receiver for chaining.

The existing Docker-gated (proto_companion_required) tests that exercise the real pipeline are unchanged.

Downstream consumer

service-python-fastapi (issue #14) will switch its gRPC-server Sync to proto.NewBuf(ctx, s.Location).WithoutOpenAPI() once this lands — follow-up from codefly-dev/service-python-fastapi#15.

🤖 Generated with Claude Code

proto.(*Buf).Generate runs an OpenAPI post-generation stage after buf
generate: it promotes a generated Swagger document to the canonical
OpenAPI path and derives TypeScript types from every openapi/*.swagger.json.
That is correct for services that want TS types from their REST API, but a
non-TypeScript service that owns a proto tree for gRPC stubs and also ships
an unrelated openapi/ REST contract incurs it too — dropping a stray
openapi/api.ts on every Sync.

Add a fluent WithoutOpenAPI opt-out, mirroring the existing With* builder
methods, that short-circuits the stage. buf dep update / buf generate and
the generated-dir cleanup are unchanged, and the default still runs the
OpenAPI pipeline. The stage is extracted verbatim into a guarded
emitOpenAPIArtifacts method so the opt-out is unit-testable offline.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@antoinetoussaint-byte antoinetoussaint-byte added the lazybox:w:6eb7d4b0145d25d1d17f:557d987346:6a8e5919 Claimed by a lazybox agent; expires without a heartbeat. label Aug 26, 2026
antoinetoussaint-byte and others added 2 commits August 25, 2026 22:13
…test

TestReaperReapsLegacyGroupWhenOwnerPidReused gated the owner spawn on
wall-clock time.Now, but the reaper authenticates owners through
processStartUnixSeconds, which derives the start second from /proc and
truncates the kernel's clock-tick start time to whole seconds. A process
launched a wall-clock second past the record's spawn second could still
read back as that same second, so legacyOwnerAlive treated the recycled
PID as the live owner and preserved the group — leaving it alive and
failing the test on Linux CI.

Spawn the owner in a loop that verifies processStartUnixSeconds reports a
start second strictly greater than the record's, killing and retrying
until it does. This gates on the reaper's own clock, removing the
wall-clock/proc skew race.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…st message

The WithoutOpenAPI opt-out documented that it skips the OpenAPI stage and
leaves inputs untouched, but was silent on what happens to an artifact a
prior opted-in run already generated and committed (e.g. openapi/api.ts):
it lingers as a stale orphan with no guidance. Auto-deleting it on the
skip path was rejected as the fix — the stage emits <base>.ts next to
<base>.swagger.json, so a name-matched delete cannot tell a stale
generated file from one a teammate later authored at that path, and
per-Sync deletion would be silent data loss that contradicts the
method's 'leaves inputs untouched' contract. The real fix is to make the
one-time manual cleanup explicit in the method doc.

Also reword two opt-out test failure messages that printed the observed
bad value as if it were the requirement, so a failing run reads forward.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@antoinetoussaint-byte
antoinetoussaint-byte merged commit 4f97824 into main Aug 26, 2026
1 check was pending
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lazybox:w:6eb7d4b0145d25d1d17f:557d987346:6a8e5919 Claimed by a lazybox agent; expires without a heartbeat.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant